package com.yitiao.controller;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <h3>SSE</h3>
 * <p>Server-Sent Events（SSE）是一种基于HTTP协议，
 * 允许服务器将数据以事件流（Event Stream）的形式发送给客户端。
 * 客户端通过建立持久的HTTP连接，并监听事件流，可以实时接收服务器推送的数据。</p>
 * <p></p>
 * <h3>注意事项</h3>
 * <p>1.为了避免长连接阻塞，一般采用异步的方式发送消息，同时使用SseEmitter对象的setTimeout()方法设置超时时间，并通过onTimeout()方法处理连接超时的逻辑。</p>
 * <p>2.避免内存占用过高，当客户端断开连接时，务必及时释放SseEmitter对象</p>
 * <p>3.可以使用SseEmitter对象的completeWithError()方法将异常信息发送给客户端，并在客户端通过eventSource.onerror事件进行处理</p>
 * <p>4.仍然有一些旧版本的浏览器不支持，需考虑兼容性</p>
 */
@RestController
@CrossOrigin
public class SseServerController {
    private static final String ANSWER = "什么是 SSE？" +
            "Server-Sent Events（SSE）是一种用于实现服务器向客户端实时推送数据的Web技术。与传统的轮询和长轮询相比，SSE提供了更高效和实时的数据推送机制。" + "</br>" +
            "SSE基于HTTP协议，允许服务器将数据以事件流（Event Stream）的形式发送给客户端。客户端通过建立持久的HTTP连接，并监听事件流，可以实时接收服务器推送的数据。" + "</br>" +
            "SSE的主要特点包括：" + "</br>" +
            "简单易用：SSE使用基于文本的数据格式，如纯文本、JSON等，使得数据的发送和解析都相对简单。" + "</br>" +
            "单向通信：SSE支持服务器向客户端的单向通信，服务器可以主动推送数据给客户端，而客户端只能接收数据。" + "</br>" +
            "实时性：SSE建立长时间的连接，使得服务器可以实时地将数据推送给客户端，而无需客户端频繁地发起请求。";

    @GetMapping(value = "/stock-price", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamStockPrice() {
        SseEmitter emitter = new SseEmitter();
        // 模拟生成实时股票价格并推送给客户端
        Random random = new Random();
        new Thread(() -> {
            try {
                while (true) {
                    // 生成随机的股票价格
                    double price = 100 + random.nextDouble() * 10;
                    // 构造股票价格的消息
                    String message = String.format("%.2f", price);
                    // 发送消息给客户端
                    emitter.send(SseEmitter.event().data(message));
                    // 休眠1秒钟
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        }).start();
        return emitter;
    }

    @GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamChatGPT() {
        AtomicInteger end = new AtomicInteger();
        SseEmitter emitter = new SseEmitter(10000000000000L);
        emitter.onTimeout(()-> System.out.println("onTimeout"));
        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        ReentrantLock lock = new ReentrantLock();
        executorService.scheduleAtFixedRate(() -> {
            try {
                Random random = new Random();
                int add = random.nextInt(10)+1;
                end.addAndGet(add);
                System.out.println(end.get());
                emitter.send(SseEmitter.event().data(ANSWER.substring(0, end.get())));
                if (end.get()>ANSWER.length()){
                    emitter.complete();
                    executorService.shutdown();
                }
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        }, 0, 500, TimeUnit.MILLISECONDS);
        return emitter;
    }
}
